package SerialTest1;

import javax.comm.*;
import javax.swing.*;
import javax.swing.event.*;

import SerialTest1.ListGUI.SendButtonCellEditor;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.TooManyListenersException;

/**
 * A class that handles the details of a serial connection. Reads from one
 * TextArea and writes to a second TextArea. Holds the state of the connection.
 */
public class SerialConnection implements Runnable, SerialPortEventListener,
		CommPortOwnershipListener {
	public SerialDemo parent;

	public SerialParameters parameters;

	public CommPortIdentifier portId;
	public SerialPort sPort;
	public final static String END_CHAT_SESSION = new Character((char) 0)
			.toString();
	public static StringBuffer toSend = new StringBuffer("");
	public static DataInputStream dis = null;
	public static BufferedReader in = null;
	public static PrintWriter out = null;
	public static File file = null;
	public static FileReader fr = null;
	public boolean open;

	public static ChatGUI cg = new ChatGUI();
	public static SendButtonCellEditor sbc = new SendButtonCellEditor();

	/**
	 * Creates a SerialConnection object and initializes variables passed in as
	 * params.
	 * 
	 * @param parent
	 *            A SerialDemo object.
	 * @param parameters
	 *            A SerialParameters object.
	 */
	public SerialConnection(SerialDemo parent, SerialParameters parameters) {
		this.parent = parent;
		this.parameters = parameters;

		open = false;
	}

	/**
	 * Attempts to open a serial connection and streams using the parameters in
	 * the SerialParameters object. If it is unsuccessful at any step it returns
	 * the port to a closed state, throws a
	 * <code>SerialConnectionException</code>, and returns.
	 * 
	 * Gives a timeout of 30 seconds on the portOpen to allow other applications
	 * to relinquish the port if have it open and no longer need it.
	 */
	public void openConnection() throws SerialConnectionException {

		// Obtain a CommPortIdentifier object for the port you want to open.
		try {
			portId = CommPortIdentifier.getPortIdentifier(parameters
					.getPortName());
		} catch (NoSuchPortException e) {
			throw new SerialConnectionException(e.getMessage());
		}

		// Open the port represented by the CommPortIdentifier object. Give
		// the open call a relatively long timeout of 30 seconds to allow
		// a different application to relinquish the port if the user
		// wants to.
		try {
			sPort = (SerialPort) portId.open("SerialDemo", 30000);
		} catch (PortInUseException e) {
			throw new SerialConnectionException(e.getMessage());
		}

		// Set the parameters of the connection. If they won't set, close the
		// port before throwing an exception.
		try {
			setConnectionParameters();
		} catch (SerialConnectionException e) {
			sPort.close();
			throw e;
		}

		try {
			sPort.addEventListener(this);
		} catch (TooManyListenersException e) {
			sPort.close();
			throw new SerialConnectionException("too many listeners added");
		}

		try {
			in = new BufferedReader(new InputStreamReader(
					sPort.getInputStream()));
			out = new PrintWriter(sPort.getOutputStream());
			dis = new DataInputStream(sPort.getInputStream());
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		sPort.notifyOnDataAvailable(true);

		// Set notifyOnBreakInterrup to allow event driven break handling.
		sPort.notifyOnBreakInterrupt(true);

		// Set receive timeout to allow breaking out of polling loop during
		// input handling.
		try {
			sPort.enableReceiveTimeout(30);
		} catch (UnsupportedCommOperationException e) {
		}

		// Add ownership listener to allow ownership event handling.
		portId.addPortOwnershipListener(this);

		open = true;
	}

	/**
	 * Sets the connection parameters to the setting in the parameters object.
	 * If set fails return the parameters object to origional settings and throw
	 * exception.
	 */
	public void setConnectionParameters() throws SerialConnectionException {

		// Save state of parameters before trying a set.
		int oldBaudRate = sPort.getBaudRate();
		int oldDatabits = sPort.getDataBits();
		int oldStopbits = sPort.getStopBits();
		int oldParity = sPort.getParity();
		int oldFlowControl = sPort.getFlowControlMode();

		// Set connection parameters, if set fails return parameters object
		// to original state.
		try {
			sPort.setSerialPortParams(parameters.getBaudRate(),
					parameters.getDatabits(), parameters.getStopbits(),
					parameters.getParity());
		} catch (UnsupportedCommOperationException e) {
			parameters.setBaudRate(oldBaudRate);
			parameters.setDatabits(oldDatabits);
			parameters.setStopbits(oldStopbits);
			parameters.setParity(oldParity);
			throw new SerialConnectionException("Unsupported parameter");
		}

		// Set flow control.
		try {
			sPort.setFlowControlMode(parameters.getFlowControlIn()
					| parameters.getFlowControlOut());
		} catch (UnsupportedCommOperationException e) {
			throw new SerialConnectionException("Unsupported flow control");
		}
	}

	/**
	 * Close the port and clean up associated elements.
	 */
	public void closeConnection() {
		// If port is already closed just return.
		if (!open) {
			try {
				in.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return;
		}

		// Remove the key listener.

		// Check to make sure sPort has reference to avoid a NPE.
		if (sPort != null) {
			// Close the port.
		
			sPort.close();

			in = null;

			// Remove the ownership listener.
			portId.removePortOwnershipListener(this);
		}

		open = false;
	}

	/**
	 * Send a one second break signal.
	 */
	public void sendBreak() {
		sPort.sendBreak(1000);
	}

	/**
	 * Reports the open status of the port.
	 * 
	 * @return true if port is open, false if port is closed.
	 */
	public boolean isOpen() {
		return open;
	}

	/**
	 * Handles SerialPortEvents. The two types of SerialPortEvents that this
	 * program is registered to listen for are DATA_AVAILABLE and BI. During
	 * DATA_AVAILABLE the port buffer is read until it is drained, when no more
	 * data is available and 30ms has passed the method returns. When a BI event
	 * occurs the words BREAK RECEIVED are written to the messageAreaIn.
	 */

	/**
	 * Handles ownership events. If a PORT_OWNERSHIP_REQUESTED event is received
	 * a dialog box is created asking the user if they are willing to give up
	 * the port. No action is taken on other types of ownership events.
	 */
	public void ownershipChange(int type) {
		if (type == CommPortOwnershipListener.PORT_OWNERSHIP_REQUESTED) {
			PortRequestedDialog prd = new PortRequestedDialog(parent);
		}
	}

	// Add text to send-buffer
	public void sendString(StringBuffer s) {
		synchronized (toSend) {
			toSend = s;
		}
	}

	public void serialEvent(SerialPortEvent e) {
		// TODO Auto-generated method stub
		byte[] readBuffer = new byte[200000];
		String s = "";
		int i;

		switch (e.getEventType()) {
		case SerialPortEvent.DATA_AVAILABLE:
			while (true) {
				try { // Poll every ~1 ms
					Thread.sleep(1);
				} catch (InterruptedException es) {
				}
				try {
					if (toSend.length() != 0) {
						out.println(String.valueOf(toSend));
						out.flush();
						toSend.setLength(0);
					}
					// Receive data

					if (in.ready()) {
						s = dis.readLine();
						ChatGUI.receiveData(s.getBytes());
					}

				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

			}

			/*
			 * case SerialPortEvent.BI: String breaks = "Send Break \n";
			 * cg.ReceiveData(breaks);
			 */

		}
	}

	public static String byteToHex(byte[] b) {
		String result = "";
		String s = new String(b);
		for (int i = 0; i < s.length(); i++) {
			result += String.format("0x%02X ", (int) s.charAt(i));
		}

		return result;
	}

	public static String byteToASCII(byte[] b) {
		String s = new String(b);
		return s;
		/*
		 * int[] iarray = new int[b.length]; int i = 0; for (byte by : b)
		 * iarray[i++] = by & 0xff; result += iarray.toString(); // "and" with
		 * 0xff since bytes are signed in java return result;
		 */
	}

	public static String byteToDecimal(byte[] b) {
		String result = "";
		// String s = new String(b);

		for (int i = 0; i < b.length; i++) {
			result += (int) b[i] + " ";
		}

		return result;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
		}
	}
}
